package com.campuscarbooking.api.service.impl;

import com.campuscarbooking.api.annotation.LogExecutionTime;
import com.campuscarbooking.api.annotation.LogMethod;
import com.campuscarbooking.api.service.LocationValidationService;
import com.campuscarbooking.api.dto.BusScheduleDTO;
import com.campuscarbooking.api.dto.PageResponse;
import com.campuscarbooking.api.dto.ReservationDTO;
import com.campuscarbooking.api.dto.request.ReservationRequest;
import com.campuscarbooking.api.exception.BusinessException;
import com.campuscarbooking.api.model.BusSchedule;
import com.campuscarbooking.api.model.Reservation;
import com.campuscarbooking.api.model.User;
import com.campuscarbooking.api.repository.BusScheduleRepository;
import com.campuscarbooking.api.repository.ReservationRepository;
import com.campuscarbooking.api.repository.UserRepository;
import com.campuscarbooking.api.service.BusScheduleService;
import com.campuscarbooking.api.service.ReservationService;
import com.campuscarbooking.api.service.SeatCacheService;
import com.campuscarbooking.api.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityNotFoundException;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class ReservationServiceImpl implements ReservationService {

    private final ReservationRepository reservationRepository;
    private final BusScheduleRepository busScheduleRepository;
    private final BusScheduleService busScheduleService;
    private final UserRepository userRepository;
    private final UserService userService;
    private final LocationValidationService locationValidationService;
    private final SeatCacheService seatCacheService;

    @Value("${business.reservation.cancelLimit:3}")
    private int cancelLimit;

    @Override
    public Page<Reservation> getUserReservations(Long userId, Reservation.Status status, Pageable pageable) {
        if (status != null) {
            return reservationRepository.findByUserIdAndStatus(userId, status, pageable);
        } else {
            return reservationRepository.findByUserId(userId, pageable);
        }
    }

    @Override
    public Reservation getUserReservation(Long reservationId, Long userId) {
        return reservationRepository.findByIdAndUserId(reservationId, userId)
                .orElseThrow(() -> new EntityNotFoundException("未找到预约信息: " + reservationId));
    }

    @Override
    @Transactional
    @LogMethod("创建预约")
    @LogExecutionTime
    public Reservation createReservation(Long userId, ReservationRequest request) {
        // Find user
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new EntityNotFoundException("未找到用户信息: " + userId));

        // Find schedule
        BusSchedule schedule = busScheduleRepository.findById(request.getScheduleId())
                .orElseThrow(() -> new EntityNotFoundException("未找到班车信息: " + request.getScheduleId()));

        // Check if schedule is available
        if (schedule.getStatus() != BusSchedule.Status.active) {
            throw new IllegalStateException("该班车不可预约");
        }

        // 首先检查座位是否可用（使用Redis读写锁确保强一致性）
        if (!seatCacheService.isSeatAvailable(schedule.getId(), Integer.valueOf(request.getSeatNumber()))) {
            throw new IllegalStateException("该座位已被预约");
        }

        // 检查是否超出容量限制
        int reservedSeatsCount = seatCacheService.getReservedSeatsCount(schedule.getId());
        if (reservedSeatsCount >= schedule.getCapacity()) {
            throw new IllegalStateException("该班车已无可用座位");
        }

        // Check if user already has a reservation for this departure time
        LocalTime departureTime = schedule.getDepartureTime();
        LocalTime startTime = departureTime.minusHours(1);
        LocalTime endTime = departureTime.plusHours(1);

        List<Reservation> existingReservations = reservationRepository.findByUserIdAndDepartureDate(
                userId, startTime, endTime);

        if (!existingReservations.isEmpty()) {
            existingReservations.stream()
                .filter(r -> r.getStatus() != Reservation.Status.canceled)
                .findAny()
                .ifPresent(r -> {
                    throw new IllegalStateException("您已有相近时间的班车预约");
                });
        }

        // 占用座位（使用Redis读写锁确保强一致性）
        boolean occupied = seatCacheService.occupySeat(schedule.getId(), Integer.valueOf(request.getSeatNumber()));
        if (!occupied) {
            throw new IllegalStateException("座位预约失败，请重试");
        }

        try {
            // Create reservation
            Reservation reservation = new Reservation();
            reservation.setUser(user);
            reservation.setSchedule(schedule);
            reservation.setStatus(Reservation.Status.pending);
            reservation.setSignedIn(false);
            reservation.setSeatNumber(request.getSeatNumber());
            reservation.setAmountPaid(schedule.getPrice());
            reservation.setPaymentStatus(Reservation.PaymentStatus.unpaid);

            if (request.getPaymentMethod() != null) {
                reservation.setPaymentMethod(request.getPaymentMethod());
            }

            reservation.setReservationTime(LocalDateTime.now());

            // Save reservation
            Reservation savedReservation = reservationRepository.save(reservation);
            
            // 记录用户预约信息到缓存
            seatCacheService.setUserReservation(savedReservation);
            
            return savedReservation;
        } catch (Exception e) {
            // 如果保存失败，释放座位
            seatCacheService.releaseSeat(schedule.getId(), Integer.valueOf(request.getSeatNumber()));
            throw e;
        }
    }

    @Override
    @Transactional
    @LogMethod("取消预约")
    @LogExecutionTime
    public Reservation cancelReservation(Long reservationId, Long userId) {
        // Find reservation
        Reservation reservation = getUserReservation(reservationId, userId);

        // Check if reservation can be cancelled
        if (reservation.getStatus() == Reservation.Status.canceled) {
            throw new IllegalStateException("预约已取消");
        }

        if (reservation.getStatus() == Reservation.Status.completed) {
            throw new IllegalStateException("已完成的预约不能取消");
        }

        // Check if it's too late to cancel (e.g., 1 hour before departure)
        LocalDateTime departureDateTime = LocalDateTime.of(
                LocalDateTime.now().toLocalDate(),
                reservation.getSchedule().getDepartureTime()
        );

        if (LocalDateTime.now().plusHours(1).isAfter(departureDateTime)) {
            throw new IllegalStateException("发车前1小时内不能取消预约");
        }

        // Check cancel limit
        int cancelCount = reservationRepository.countCancelledReservationsToday(
                userId, LocalDateTime.now().withHour(0).withMinute(0).withSecond(0));

        if (cancelCount >= cancelLimit) {
            throw new IllegalStateException("今日取消预约次数已达上限");
        }

        // Update reservation status
        reservation.setStatus(Reservation.Status.canceled);
        reservation.setCanceledAt(LocalDateTime.now());
        reservation.setCancelCount(reservation.getCancelCount() + 1);

        // 释放座位（使用Redis读写锁确保强一致性）
        Integer seatNumber = Integer.valueOf(reservation.getSeatNumber());
        boolean released = seatCacheService.releaseSeat(reservation.getSchedule().getId(), seatNumber);
        if (!released) {
            log.warn("座位释放失败: scheduleId={}, seatNumber={}", reservation.getSchedule().getId(), seatNumber);
        }

        // 删除用户预约信息缓存
        seatCacheService.removeUserReservation(reservation.getSchedule().getId(), userId);

        // Save reservation
        return reservationRepository.save(reservation);
    }

    @Override
    public ReservationDTO cancelReservation(Long id) {
        User user = userService.getCurrentUser();
        Reservation reservation = cancelReservation(id, user.getId());

        BusScheduleDTO scheduleDTO = null;
        try {
            scheduleDTO = busScheduleService.getScheduleById(reservation.getSchedule().getId());
        } catch (BusinessException e) {
            // 忽略不存在的班车
        }

        return ReservationDTO.fromEntity(reservation, scheduleDTO);
    }

    @Override
    @Transactional
    public Reservation checkInReservation(Long reservationId) {
        // Find reservation
        Reservation reservation = reservationRepository.findById(reservationId)
                .orElseThrow(() -> new EntityNotFoundException("未找到预约信息: " + reservationId));

        // Check if reservation can be checked in
        if (reservation.getStatus() != Reservation.Status.confirmed) {
            throw new IllegalStateException("预约状态不允许签到");
        }

        if (reservation.getSignedIn()) {
            throw new IllegalStateException("预约已签到");
        }

        // Update reservation
        reservation.setSignedIn(true);
        reservation.setSignInTime(LocalDateTime.now());
        reservation.setStatus(Reservation.Status.completed);

        return reservationRepository.save(reservation);
    }

    @Override
    public PageResponse<ReservationDTO> getMyReservations(
            Reservation.Status status,
            LocalTime startTime,
            LocalTime endTime,
            int page,
            int pageSize) {

        User user = userService.getCurrentUser();
        Page<Reservation> reservationsPage;

        if (status != null) {
            reservationsPage = reservationRepository.findByUserIdAndStatus(
                    user.getId(), status, PageRequest.of(page, pageSize));
        } else if (startTime != null && endTime != null) {
            reservationsPage = reservationRepository.findByDepartureTimeBetween(
                    startTime, endTime, PageRequest.of(page, pageSize));
        } else {
            reservationsPage = reservationRepository.findByUserId(
                    user.getId(), PageRequest.of(page, pageSize));
        }

        List<ReservationDTO> reservations = reservationsPage.getContent().stream()
                .map(reservation -> {
                    BusScheduleDTO scheduleDTO = null;
                    if (reservation.getSchedule() != null) {
                        try {
                            scheduleDTO = busScheduleService.getScheduleById(reservation.getSchedule().getId());
                        } catch (BusinessException e) {
                            // 忽略不存在的班车
                        }
                    }
                    return ReservationDTO.fromEntity(reservation, scheduleDTO);
                })
                .collect(Collectors.toList());

        return PageResponse.from(reservationsPage, reservations);
    }

    @Override
    @Transactional
    public ReservationDTO signIn(Long id, String signInMethod, Double latitude, Double longitude) {
        User user = userService.getCurrentUser();
        Reservation reservation = reservationRepository.findByIdAndUserId(id, user.getId())
                .orElseThrow(() -> new BusinessException("预约不存在"));

        if (reservation.getStatus() != Reservation.Status.confirmed) {
            throw new BusinessException("预约状态不正确，无法签到");
        }

        if (reservation.getSignedIn()) {
            throw new BusinessException("已经签到过了");
        }

        boolean signInValid = false;

        if ("location".equals(signInMethod)) {
            // 使用位置验证服务进行验证
            signInValid = locationValidationService.validateLocation(
                reservation.getSchedule().getId(),
                latitude,
                longitude
            );
        } else {
            // 其他签到方式
            signInValid = true;
        }

        if (!signInValid) {
            throw new BusinessException(40008, "签到失败：位置验证未通过");
        }

        // 更新预约状态
        reservation.setSignedIn(true);
        reservation.setSignInTime(LocalDateTime.now());

        Reservation signedInReservation = reservationRepository.save(reservation);

        // 获取班车信息
        BusScheduleDTO scheduleDTO = null;
        try {
            scheduleDTO = busScheduleService.getScheduleById(signedInReservation.getSchedule().getId());
        } catch (BusinessException e) {
            // 忽略不存在的班车
        }

        return ReservationDTO.fromEntity(signedInReservation, scheduleDTO);
    }

    @Override
    public ReservationDTO createReservation(Long scheduleId, String seatNumber, String paymentMethod) {
        User user = userService.getCurrentUser();

        ReservationRequest request = new ReservationRequest();
        request.setScheduleId(scheduleId);
        request.setSeatNumber(seatNumber);
        if (paymentMethod != null) {
            request.setPaymentMethod(Reservation.PaymentMethod.valueOf(paymentMethod));
        }

        Reservation reservation = createReservation(user.getId(), request);

        BusScheduleDTO scheduleDTO = null;
        try {
            scheduleDTO = busScheduleService.getScheduleById(reservation.getSchedule().getId());
        } catch (BusinessException e) {
            // 忽略不存在的班车
        }

        return ReservationDTO.fromEntity(reservation, scheduleDTO);
    }
}